package com.hg.miaosha.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.plugins.Page;
import com.hg.miaosha.common.aop.RateLimit;
import com.hg.miaosha.common.aop.Servicelock;
import com.hg.miaosha.common.enums.SeckillEnums;
import com.hg.miaosha.common.exception.SeckillException;
import com.hg.miaosha.common.util.PageUtils;
import com.hg.miaosha.dto.PageDTO;
import com.hg.miaosha.entity.Seckill;
import com.hg.miaosha.entity.SuccessKilled;
import com.hg.miaosha.repository.SeckillRepository;
import com.hg.miaosha.repository.SuccessKilledRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * created by skh on 2019/6/15
 */
@Service
@Slf4j
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private SeckillRepository seckillRepository;

    @Autowired
    private SuccessKilledRepository successKilledRepository;

    Lock lock = new ReentrantLock(true); //使用公平锁

    @Override
    public PageUtils getSeckillGoodsList(PageDTO pageDTO) {
        Page page = seckillRepository.getSeckillGoodsList(pageDTO);
        return new PageUtils(page);
    }

    @Override
    @Transactional
    public void startSeckill(Integer seckillId, Integer userId) {
        executeSecKill(seckillId, userId);
    }

    @Override
    @Transactional
    public synchronized void startWithSynchronized(Integer seckillId, Integer userId) {
        executeSecKill(seckillId, userId);
    }


    @Override
    @Transactional
    public void startWithLock(Integer seckillId, Integer userId) {
        try {
            lock.lock();
            executeSecKill(seckillId, userId);
        } finally {
            lock.unlock();
        }
    }

    @Override
    @Transactional
    @Servicelock
    public void startWithAOPLock(Integer seckillId, Integer userId) {
        executeSecKill(seckillId, userId);
    }

    @Override
    @Transactional
    public void startWithPessimisticLock(Integer seckillId, Integer userId) {
        log.info("开始秒杀,商品id:{},用户id:{}", seckillId, userId);

        //查询库存是否充足
        Seckill seckill = seckillRepository.getGoodsStockBySeckillIdWithPessimisticLock(seckillId);

        checkSeckillCondition(seckill);

        //扣库存
        seckillRepository.decreaseStockBySeckillId(seckillId);

        //创建订单
        SuccessKilled successKilled = new SuccessKilled();
        successKilled.setSeckillId(seckillId);
        successKilled.setUserId(userId);
        successKilled.setState(SeckillEnums.SECKILL_SUCCESS.getCode());
        successKilled.setCreateTime(new Date());
        successKilledRepository.createSeckilledOrder(successKilled);

        log.info("新建秒杀订单成功,秒杀商品id:{},用户id:{}", seckillId, userId);

    }

    @Override
    @Transactional
    public void startWithPessimisticLock2(Integer seckillId, Integer userId) {
        Integer result = seckillRepository.updateSeckillNumberBySecKillId(seckillId);

        if (result > 0) {
            //创建订单
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState(SeckillEnums.SECKILL_SUCCESS.getCode());
            successKilled.setCreateTime(new Date());
            successKilledRepository.createSeckilledOrder(successKilled);

            log.info("新建秒杀订单成功,秒杀商品id:{},用户id:{}", seckillId, userId);
        } else {
            throw new SeckillException(SeckillEnums.SECKILL_ERROR_WITH_NO_STOCK);
        }

    }

    @Override
    @Transactional
    public void startWithOptimisticLock(Integer seckillId, Integer userId) {
        Seckill goodsStockBySeckillId = seckillRepository.getGoodsStockBySeckillId(seckillId);
        if (goodsStockBySeckillId.getNumber() > 0) {
            Integer result = seckillRepository.updateSeckillNumberBySecKillIdWithOptimisticLock(seckillId, goodsStockBySeckillId.getVersion());
            if (result > 0) {
                //创建订单
                SuccessKilled successKilled = new SuccessKilled();
                successKilled.setSeckillId(seckillId);
                successKilled.setUserId(userId);
                successKilled.setState(SeckillEnums.SECKILL_SUCCESS.getCode());
                successKilled.setCreateTime(new Date());
                successKilledRepository.createSeckilledOrder(successKilled);

                log.info("新建秒杀订单成功,秒杀商品id:{},用户id:{}", seckillId, userId);
                return;
            } else {
                throw new SeckillException("秒杀冲突");
            }
        } else {
            throw new SeckillException(SeckillEnums.SECKILL_ERROR_WITH_NO_STOCK);
        }
    }

    @Override
    @RateLimit(limitNum = 10)
    public void testRateLimit() {

        System.out.println("业务执行成功");
    }


    private void checkSeckillCondition(Seckill seckill) {
        if (seckill == null) {
            throw new SeckillException(SeckillEnums.SECKILL_ERROR_WITH_GOODS_NOT_FOUND);
        }

        if (seckill.getNumber() <= 0) {
            throw new SeckillException(SeckillEnums.SECKILL_ERROR_WITH_NO_STOCK);
        }
    }

    private void executeSecKill(Integer seckillId, Integer userId) {
        log.info("开始秒杀,商品id:{},用户id:{}", seckillId, userId);

        //查询库存是否充足
        Seckill seckill = seckillRepository.getGoodsStockBySeckillId(seckillId);

        checkSeckillCondition(seckill);

        //扣库存
        seckillRepository.decreaseStockBySeckillId(seckillId);

        //创建订单
        SuccessKilled successKilled = new SuccessKilled();
        successKilled.setSeckillId(seckillId);
        successKilled.setUserId(userId);
        successKilled.setState(SeckillEnums.SECKILL_SUCCESS.getCode());
        successKilled.setCreateTime(new Date());
        successKilledRepository.createSeckilledOrder(successKilled);

        log.info("新建秒杀订单成功,秒杀商品id:{},用户id:{}", seckillId, userId);
    }

    @Override
    @Transactional
    public void initData() {
        //删除所有订单
        successKilledRepository.deleteAllOrder();
        //恢复商品库存
        List<Seckill> seckillList = seckillRepository.getAllSeckillGoodsList();
        if (CollUtil.isEmpty(seckillList)) {
            return;
        }
        for (Seckill seckill : seckillList) {
            seckill.setNumber(1000);
            seckillRepository.updateSeckillById(seckill);
        }
    }

    @Override
    public Integer countSuccessSecKill() {
        return successKilledRepository.countSuccessSecKill();

    }


}
